/**
 * 
 */
package com.freewheelycheckin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;



import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.app.FragmentTransaction;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;

import android.text.Html;
import android.text.format.DateFormat;
import android.text.format.Time;
import android.text.method.LinkMovementMethod;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author R!
 *
 */
public class LocationLogger extends Activity {
	TextView mTvCurrLoc=null;
	TextView mTvLocLog=null;
	EditText mLocLogComment=null;
	Button btnLogLoc=null;
	Account userAccount=null;
	
	private Handler mHandler_OnLoc;
	
	LocationManager mLocationManager=null;
	Location mLocationCurrent= null;
	
	private static final int FIVE_SECONDS = 5000;
	private static final int ACCURACY_IN_METERS = 1;
	private static final int THIRTY_SECONDS = 30000;//1000 * 60 * 2;
	
	private final String APP_BASE_URL="frwheely.appspot.com";
	private final String APP_LOC_LOG_URL="/LocLogServlet?opcode=0";
	private Handler mHandler_OnAuth= null;
	
	DefaultHttpClient http_client = new DefaultHttpClient();
	
	private ProgressDialog mProgressDialog = null;
	private boolean locEnabled=false;
	AlertDialog mLocSettingsDialog=null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		setup();
		
	}
	@Override
	protected void onResume(){
		super.onResume();
		if(!locEnabled){
			//initialize GPS with default as network
			initializeGPS(LocationManager.NETWORK_PROVIDER);
		}
	}

	@SuppressLint("HandlerLeak")
	private void setup() {
		
		mLocLogComment = (EditText)findViewById(R.id.edtlocComment);
		mTvCurrLoc = (TextView)findViewById(R.id.tvCurrLoc);
		mTvCurrLoc.setMovementMethod(LinkMovementMethod.getInstance());
		mTvLocLog = (TextView)findViewById(R.id.tvLocLog);
		btnLogLoc = (Button)findViewById(R.id.btnLogLoc);
		if(mLocationManager!=null)
		mLocationManager.removeUpdates(listener);
		// Handler for updating text fields on the UI like the lat/long and address.
        mHandler_OnLoc = new Handler() {
            public void handleMessage(Message msg) {
            	if(msg.obj!=null){
            		updateLocation ((Location)msg.obj);
	                
            		enableLocationLogging(true);
            	}
            	else
            	{
            		enableLocationLogging(false);
            	}
            }
        };
        
        mProgressDialog = new ProgressDialog(this);
   	 	mProgressDialog.setCancelable(false);
        //get stored user account from somewhere?
        intializeUserInfo();
		
		//disable textbox and log button till location is received
        enableLocationLogging(false);
        
        //initialize GPS with default as network
        initializeGPS(LocationManager.NETWORK_PROVIDER);
		
	}
	private void intializeUserInfo() {
		
		Intent intent= getIntent();
		if(intent.getExtras().get("account")!=null)
		userAccount=(Account)intent.getExtras().get("account");
		AccountManager accountManager = AccountManager.get(getApplicationContext());
		accountManager.getAuthToken(userAccount, "ah", false, new GetAuthTokenCallback(), null);
	
		
		//initializeGPS();
	}
	
	private void initializeGPS(String locationProvider) {
		mLocationManager= (LocationManager)getSystemService(Context.LOCATION_SERVICE);
		locEnabled = mLocationManager.isProviderEnabled(locationProvider);
		if(!locEnabled){
			
			if(mLocSettingsDialog!=null && mLocSettingsDialog.isShowing()){
				mLocSettingsDialog.dismiss();
			}
			
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			
			builder.setTitle(R.string.enable_gps);
			builder.setMessage(R.string.enable_gps_dialog);
            builder.setPositiveButton(R.string.enable_gps, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                	mLocSettingsDialog.hide();
                    enableLocationSettings();
                }
            });
            mLocSettingsDialog= builder.create();
            mLocSettingsDialog.show();
		}
		else{
			//now that GPS is enabled get latest location
			//requestUpdatesFromProvider(LocationManager.GPS_PROVIDER,R.string.not_support_gps);
			requestUpdatesFromProvider(LocationManager.NETWORK_PROVIDER,R.string.not_support_gps);

		}
		
		
		
		
	}
	
	private void enableLocationSettings() {
	    Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
	    startActivity(settingsIntent);
	}
	/**
     * Method to register location updates with a desired location provider.  If the requested
     * provider is not available on the device, the app displays a Toast with a message referenced
     * by a resource id.
     *
     * @param provider Name of the requested provider.
     * @param errorResId Resource id for the string message to be displayed if the provider does
     *                   not exist on the device.
     * @return A previously returned {@link android.location.Location} from the requested provider,
     *         if exists.
     */
    private void requestUpdatesFromProvider(final String provider, final int errorResId) {
        Location location = null;
        if (mLocationManager.isProviderEnabled(provider)) {
            mLocationManager.requestLocationUpdates(provider, FIVE_SECONDS, ACCURACY_IN_METERS, listener);
            location = mLocationManager.getLastKnownLocation(provider);
            
        } else {
            Toast.makeText(this, errorResId, Toast.LENGTH_LONG).show();
        }
        // if the location is less than 30 secs old update UI
        Time now=(new Time());
        now.setToNow();
        if(location !=null&&(now.toMillis(false)-location.getTime()<THIRTY_SECONDS)){
        	//location is obtained now update the UI
            updateLocation(location);
        }
        
    }
	private final LocationListener listener = new LocationListener() {

	    @Override
	    public void onLocationChanged(Location location) {
	        // A new location update is received.  Do something useful with it.  In this case,
	        // we're sending the update to a handler which then updates the UI with the new
	        // location.
	        Message.obtain(mHandler_OnLoc,
	                0,
	                location).sendToTarget();
	        
	            
	        }

		public void onProviderDisabled(String provider) {
		
		}

		@Override
		public void onProviderEnabled(String provider) {
			
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			
		}
	    
	};
	

	
	private void updateLocation(Location location){
		mLocationCurrent = location;
        mTvCurrLoc.setText(Html.fromHtml("<a href=\"http://maps.google.com/?q="+mLocationCurrent.getLatitude()+","+mLocationCurrent.getLongitude()+"\">"+mLocationCurrent.getLatitude()+","+mLocationCurrent.getLongitude()+"</a>"));
	}
	private void enableLocationLogging(boolean enable){
		btnLogLoc.setEnabled(enable);
		mLocLogComment.setEnabled(enable);
	}
	
	
	

	/** Called when the user clicks the Log button */
	public void logLocation(View view) {
	 
		String loggingUrl= "http://"+APP_BASE_URL+APP_LOC_LOG_URL;
		String sLat= Double.toString( mLocationCurrent.getLatitude());
		String sLongi= Double.toString( mLocationCurrent.getLongitude());
		String comment= mLocLogComment.getText().toString();
		new LogLocationTask().execute(loggingUrl,sLat,sLongi,comment);
	}
	
	
	 private class GetAuthTokenCallback implements AccountManagerCallback<Bundle> {
         public void run(AccountManagerFuture<Bundle> result) {
                 Bundle bundle;
                 Intent intent;
                 try {
                         bundle = (Bundle) result.getResult();
                         if(bundle.containsKey(AccountManager.KEY_INTENT)){
                        	 intent = (Intent)bundle.get(AccountManager.KEY_INTENT);
                        	 startActivity(intent);
                         }else {
                                 onGetAuthToken(bundle);
                         }
                 } catch (OperationCanceledException e) {
                         
                         e.printStackTrace();
                 } catch (AuthenticatorException e) {
                         
                         e.printStackTrace();
                 } catch (IOException e) {
                         
                         e.printStackTrace();
                 }
         }
 };
	 protected void onGetAuthToken(Bundle bundle) {
	     String auth_token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
	     new GetCookieTask().execute(auth_token);
	}
	 private class GetCookieTask extends AsyncTask<String,Boolean,Boolean> {
         protected Boolean doInBackground(String... tokens) {
                 try {
                         // Don't follow redirects
                         http_client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false);
                         
                         HttpGet http_get = new HttpGet("http://"+APP_BASE_URL+"/_ah/login?continue=http://"+APP_BASE_URL+"/&auth=" + tokens[0]);
                         org.apache.http.HttpResponse response=null;
                         response = http_client.execute(http_get);
                         if(response.getStatusLine().getStatusCode() != 302)
                                 // Response should be a redirect
                                 return false;
                         
                         for(Cookie cookie : http_client.getCookieStore().getCookies()) {
                                 if(cookie.getName().equals("ACSID"))
                                         return true;
                         }
                 } catch (ClientProtocolException e) {
                         // TODO Auto-generated catch block
                         e.printStackTrace();
                 } catch (IOException e) {
                         // TODO Auto-generated catch block
                         e.printStackTrace();
                 } finally {
                         http_client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, true);
                 }
                 return false;
         }
         @Override
         protected void onPreExecute() {
        	 mProgressDialog.setTitle("Please wait...");
        	 mProgressDialog.setMessage("Authenticating..");
        	 mProgressDialog.show();
        	 
         }
         protected void onPostExecute(Boolean result) {
             //everything is authenticated now go get our stufff    
        	 Message.obtain(mHandler_OnAuth,0,http_client.getCookieStore().getCookies());
        	 Toast.makeText(getApplicationContext(), "Authentication succeeded!", Toast.LENGTH_SHORT).show();
        	// new AuthenticatedRequestTask().execute("http://yourapp.appspot.com/admin/");
        	 
        	 mProgressDialog.dismiss();
         }

        
 }
private class LogLocationTask extends AsyncTask<String,Boolean,HttpResponse> {
        
		 String lat;
		 String longi;
		 String comment;
		 @Override
         protected org.apache.http.HttpResponse doInBackground(String... obj) {
        	 
                 try {
                        HttpPost http_post= new HttpPost(new URI(obj[0]));
                        lat=obj[1];
                        longi=obj[2];
                        comment=obj[3];
                        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
                        nameValuePairs.add(new BasicNameValuePair("lat",lat));
                        nameValuePairs.add(new BasicNameValuePair("longi",longi));
                        nameValuePairs.add(new BasicNameValuePair("comment",comment));
                        http_post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                        return http_client.execute(http_post);
                 } catch (ClientProtocolException e) {
                         e.printStackTrace();
                 } catch (IOException e) {
                         
                         e.printStackTrace();
                 } catch (URISyntaxException e) {
					
					e.printStackTrace();
				}
                 return null;
         }
         @Override
         protected void onPreExecute() {
        	 mProgressDialog.setMessage("Logging location....");
        	 mProgressDialog.show();
         }
        
         protected void onPostExecute(HttpResponse result) {
                
        	 try {
                         BufferedReader reader = new BufferedReader(new InputStreamReader(result.getEntity().getContent()));
                         String first_line = reader.readLine();
                         mProgressDialog.dismiss();
                         Toast.makeText(getApplicationContext(), first_line, Toast.LENGTH_LONG).show();      
                         //update the last logged comment
                         updateLocationLog(lat,longi,comment);
                         //clear the comment to prevent repition
                         mLocLogComment.setText("");
                         //hide keyboard
                         InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                         inputManager.hideSoftInputFromWindow(mLocLogComment.getWindowToken(), 0);
                 } catch (IllegalStateException e) {
                        
                         e.printStackTrace();
                 } catch (IOException e) {
                        
                         e.printStackTrace();
                 }
         }
 }
	/** Determines whether one Location reading is better than the current Location fix.
     * Code taken from
     * http://developer.android.com/guide/topics/location/obtaining-user-location.html
     *
     * @param newLocation  The new Location that you want to evaluate
     * @param currentBestLocation  The current Location fix, to which you want to compare the new
     *        one
     * @return The better Location object based on recency and accuracy.
     */
   protected Location getBetterLocation(Location newLocation, Location currentBestLocation) {
       if (currentBestLocation == null) {
           // A new location is always better than no location
           return newLocation;
       }

       // Check whether the new location fix is newer or older
       long timeDelta = newLocation.getTime() - currentBestLocation.getTime();
       boolean isSignificantlyNewer = timeDelta > THIRTY_SECONDS;
       boolean isSignificantlyOlder = timeDelta < -THIRTY_SECONDS;
       boolean isNewer = timeDelta > 0;

       // If it's been more than two minutes since the current location, use the new location
       // because the user has likely moved.
       if (isSignificantlyNewer) {
           return newLocation;
       // If the new location is more than two minutes older, it must be worse
       } else if (isSignificantlyOlder) {
           return currentBestLocation;
       }

       // Check whether the new location fix is more or less accurate
       int accuracyDelta = (int) (newLocation.getAccuracy() - currentBestLocation.getAccuracy());
       boolean isLessAccurate = accuracyDelta > 0;
       boolean isMoreAccurate = accuracyDelta < 0;
       boolean isSignificantlyLessAccurate = accuracyDelta > 200;

       // Check if the old and new location are from the same provider
       boolean isFromSameProvider = isSameProvider(newLocation.getProvider(),
               currentBestLocation.getProvider());

       // Determine location quality using a combination of timeliness and accuracy
       if (isMoreAccurate) {
           return newLocation;
       } else if (isNewer && !isLessAccurate) {
           return newLocation;
       } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
           return newLocation;
       }
       return currentBestLocation;
   }

   /** Checks whether two providers are the same */
   private boolean isSameProvider(String provider1, String provider2) {
       if (provider1 == null) {
         return provider2 == null;
       }
       return provider1.equals(provider2);
   }
   
   
   private void updateLocationLog(String lat, String longi, String comment){
	   
	   String currLog= mTvLocLog.getText().toString();
	   
	   Date d = new Date();
	   CharSequence s  = DateFormat.format("MM-dd-yyyy hh:mm:ss", d.getTime());
	   currLog+="\nOn:"+s.toString()+"\nLocation("+lat+","+longi+");Comment:"+(comment.length()>=10?comment.substring(0,10)+"...":comment);
	   mTvLocLog.setText(currLog);
	   
   }
   private void clearLocationLog(){
	   mTvLocLog.setText("No locations logged.");
   }
   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
       MenuInflater inflater = getMenuInflater();
       inflater.inflate(R.menu.main_menu, menu);
       return true;
   }
   @Override
   public boolean onOptionsItemSelected(MenuItem item) {
       // Handle item selection
       switch (item.getItemId()) {
           case R.id.providerGps:
        	   initializeGPS(LocationManager.GPS_PROVIDER);
               return true;
           case R.id.providerNetwork:
        	   initializeGPS(LocationManager.NETWORK_PROVIDER);
               return true;
           case R.id.clearLog:
               clearLocationLog();
               return true;
           default:
               return super.onOptionsItemSelected(item);
       }
   }
}
